﻿using System;
using System . Collections . Generic;
using System . Collections . ObjectModel;
using System . Linq;
using System . Text;
using System . Xml . Linq;
using System . Runtime . Serialization;
using WenceyWang . RichMan4L . Players . Models;
using WenceyWang . RichMan4L . Players . Events;
using WenceyWang . RichMan4L . Auctions;
using WenceyWang . RichMan4L . Stocks;
using WenceyWang . RichMan4L . Maps;

namespace WenceyWang . RichMan4L . Players
{
	//Todo:玩家拥有的区块列表
	public class Player : GameObject
	{
		/// <summary>
		/// 玩家的名称
		/// </summary>
		public string Name { get { return Model . Name; } }

		/// <summary>
		/// 玩家的模型
		/// </summary>

		public PlayerModel Model { get; private set; }

		/// <summary>
		/// 指示玩家能否获得收益
		/// </summary>
		public bool CanGetCharge
		{
			get
			{
				if ( State == PlayerState . Normal )
				{
					return this . IsBlockGetCharge ( );
				}
				else
				{
					return false;
				}
			}
		}

		#region State

		/// <summary>
		/// 玩家的状态
		/// </summary>
		public PlayerState State { get; protected set; }


		public long? StateDuration { get; protected set; }


		public long? StateStartDate { get; protected set; }

		#endregion

		#region Money

		public List<long> MoneyHistory { get; set; }

		private long money;

		public long Money
		{
			get { return money; }
			private set
			{
				money = value;
				if ( money < 0 )
				{
					Dispose ( );
				}
			}
		}

		public long PropertiesInMoney
		{
			get
			{
				return Money + SavedMoney . Sum ( ( proof ) => proof . MoneyToGet ) - BorrowedMoney . Sum ( ( proof ) => proof . MoneyToReturn );
			}
		}

		#endregion

		public int MaxNumberOfDice { get; set; } = 1;

		public long GamePoint { get; set; } = 0;

		public List<Cards . Card> Cards { get; protected set; } = new List<Cards . Card> ( );

		public Dictionary<Stocks . Stock , long> Stocks { get; protected set; } = new Dictionary<Stocks . Stock , long> ( );

		public List<Area> Areas { get; protected set; } = new List<Area> ( );

		public List<Banks . SavingBankProof> SavedMoney { get; set; } = new List<Banks . SavingBankProof> ( );

		public void GetFromSaving ( Banks . SavingBankProof proof )
		{
			if ( disposedValue )
			{
				throw new ObjectDisposedException ( nameof ( Player ) );
			}
			if ( proof == null )
			{
				throw new ArgumentNullException ( nameof ( proof ) );
			}
			Money += proof . MoneyToGet;
			SavedMoney . Remove ( proof );
		}

		public List<Banks . BorrowingBankProof> BorrowedMoney { get; set; } = new List<Banks . BorrowingBankProof> ( );

		public void PayForBorrowing ( Banks . BorrowingBankProof proof )
		{
			if ( disposedValue )
			{
				throw new ObjectDisposedException ( nameof ( Player ) );
			}
			if ( proof == null )
			{
				throw new ArgumentNullException ( nameof ( proof ) );
			}

			Money -= proof . MoneyToReturn;
			BorrowedMoney . Remove ( proof );
		}


		public Maps . Road Position { get; set; }

		public Maps . Road PreviousPosition { get; set; }


		public List<Buffs . PlayerBuff> Buffs { get; private set; } = new List<Buffs . PlayerBuff> ( );

		public bool HaveMoveToday { get; set; } = false;

		public bool CanMove { get { return ( !HaveMoveToday ) && State == PlayerState . Normal && this . IsBlockMoving ( ); } }

		/// <summary>
		/// 移动
		/// </summary>
		/// <param name="moveType">移动类型</param>
		public virtual void Move ( MoveType moveType )
		{
			if ( disposedValue )
			{
				throw new ObjectDisposedException ( nameof ( Player ) );
			}
			if ( ( int ) moveType > MaxNumberOfDice )
			{
				throw new ArgumentOutOfRangeException ( nameof ( moveType ) );
			}

			if ( CanMove )
			{
				HaveMoveToday = true;
				List<int> diceResult = Game . Current . GameEnviroment . GetDice ( ( int ) moveType );
				var route = Position . Route ( PreviousPosition , diceResult . Sum ( ) );
				Position = route . Terminal;
				PreviousPosition = route . Previous;
				Position . Stay ( this );
				MoveEvent?.Invoke ( this , new PlayerMoveEventArgs ( route , diceResult ) );
			}
			else
			{
				MoveEvent?.Invoke ( this , new PlayerMoveEventArgs ( new Maps . Path ( ) , new List<int> ( ) ) );
			}
		}

		public event EventHandler<PlayerMoveEventArgs> MoveEvent;


		/// <summary>
		/// 购买某个区域
		/// </summary>
		/// <param name="toBuy">要购买的区域</param>
		/// <returns></returns>
		public BuyAreaResult BuyArea ( Area toBuy )
		{
			if ( disposedValue )
			{
				throw new ObjectDisposedException ( nameof ( Player ) );
			}
			if ( toBuy == null )
			{
				throw new ArgumentNullException ( nameof ( toBuy ) );
			}


			BuyAreaResult result = new BuyAreaResult
			{
				Area = null ,
				Money = 0 ,
			};


			if ( !toBuy . IsBlockBuy ( ) )
			{
				if ( !this . IsBlockBuyArea ( ) )
				{
					if ( toBuy . Owner == null )
					{
						if ( Money >= toBuy . Price )
						{
							Money -= toBuy . Price;
							result . Area = toBuy;
							toBuy . Owner = this;
							result . StatusCode = BuyAreaStatusCode . Success;
							result . Money = toBuy . Price;
						}
						else
						{
							result . StatusCode = BuyAreaStatusCode . MoneyNotEnough;
						}
					}
					else
					{
						result . StatusCode = BuyAreaStatusCode . NotBuyable;
					}
				}
				else
				{
					result . StatusCode = BuyAreaStatusCode . PlayerDebuff;
				}
			}
			else
			{
				result . StatusCode = BuyAreaStatusCode . AreaDebuff;
			}
			BuyAreaEvent?.Invoke ( this , new PlayerBuyAreaEventArgs ( result ) );
			return result;
		}

		public event EventHandler<PlayerBuyAreaEventArgs> BuyAreaEvent;

		public void BuildBuilding ( Area position , Type builing )
		{
			if ( position == null )
			{
				throw new ArgumentNullException ( nameof ( position ) );
			}
			if ( position . Owner != this )
			{
				throw new ArgumentException ( "Area should owned by player" , nameof ( position ) );
			}
		}

		public BuyStockResult BuyStock ( Stocks . Stock toBuy , long number )
		{
			if ( disposedValue )
			{
				throw new ObjectDisposedException ( nameof ( Player ) );
			}

			if ( toBuy == null )
			{
				throw new ArgumentNullException ( nameof ( toBuy ) );
			}

			BuyStockResult result = new BuyStockResult
			{
				Money = 0 ,
				Number = 0
			};

			if ( !this . IsBlockBuyStock ( ) )
			{
				if ( !toBuy . IsBlockBuy ( ) )
				{
					result . Number = Convert . ToInt64 ( Math . Floor ( ( Money - 1 ) / toBuy . Price ) );
					result . Money = Convert . ToInt64 ( Math . Ceiling ( result . Number * toBuy . Price ) );
					Money -= result . Money;
					if ( result . Number == number )
					{
						result . StatusCode = BuyStockStatusCode . Success;
					}
					else
					{
						result . StatusCode = BuyStockStatusCode . MoneyNotEnough;
					}
				}
				else
				{
					result . StatusCode = BuyStockStatusCode . StockDebuff;
				}
			}
			else
			{
				result . StatusCode = BuyStockStatusCode . PlayerDebuff;
			}
			if ( Stocks . ContainsKey ( toBuy ) )
			{
				Stocks [ toBuy ] += result . Number;
			}
			else
			{
				Stocks . Add ( toBuy , result . Number );
			}
			BuyStockEvent?.Invoke ( this , new PlayerBuyStockEventArgs ( result ) );
			return result;
		}

		public event EventHandler<PlayerBuyStockEventArgs> BuyStockEvent;


		public void ChangeState ( PlayerState state , long duration )
		{
			if ( disposedValue )
			{
				throw new ObjectDisposedException ( nameof ( Player ) );
			}
			if ( duration < 1 )
			{
				throw new ArgumentOutOfRangeException ( nameof ( duration ) );
			}
			var oldState = State;
			State = state;
			StateStartDate = Game . Current . Calendar . Today . Date;
			StateDuration = duration;
			ChangeStateEvent?.Invoke ( this , new PlayerChangeStateEventArgs ( oldState , State , duration ) );
		}

		public event EventHandler<PlayerChangeStateEventArgs> ChangeStateEvent;

		/// <summary>
		/// 支付建筑的维持费
		/// </summary>
		/// <param name="building">要支付维持费的建筑</param>
		/// <param name="money">要支付的数额</param>
		public void PayForBuilding ( Maps . Buildings . Building building , long money )
		{
			if ( disposedValue )
			{
				throw new ObjectDisposedException ( nameof ( Player ) );
			}
			if ( building == null )
			{
				throw new ArgumentNullException ( nameof ( building ) );
			}
			if ( money < 0 )
			{
				throw new ArgumentOutOfRangeException ( nameof ( money ) );
			}

			Money -= money;
			PayForBuildingEvent?.Invoke ( this , new PlayerPayForBuildingEventArgs ( building , money ) );
		}

		public event EventHandler<PlayerPayForBuildingEventArgs> PayForBuildingEvent;

		/// <summary>
		/// 支付过路费
		/// </summary>
		/// <param name="area">收取过路费的区块</param>
		/// <param name="money">支付的数额</param>
		public void PayForCross ( Area area , long money )
		{
			if ( disposedValue )
			{
				throw new ObjectDisposedException ( nameof ( Player ) );
			}
			if ( area == null )
			{
				throw new ArgumentNullException ( nameof ( area ) );
			}
			if ( money < 0 )
			{
				throw new ArgumentOutOfRangeException ( nameof ( money ) );
			}

			Money -= money;
			Game . Current . GameEnviroment . PlayerSay ( this , Model . GetSayingWhenHarmed ( area . Owner . Model ) );
			PayForCrossEvent?.Invoke ( this , new PlayerPayForCrossEventArgs ( area , money ) );
		}

		public event EventHandler<PlayerPayForCrossEventArgs> PayForCrossEvent;

		/// <summary>
		/// 从区块获得收益
		/// </summary>
		/// <param name="area">带来收益的区块</param>
		/// <param name="player">带来收益的玩家，如没有，则为null</param>
		/// <param name="money">收益的数额</param>
		public void GetFromArea ( Area area , Player player , long money )
		{
			if ( disposedValue )
			{
				throw new ObjectDisposedException ( nameof ( Player ) );
			}
			if ( area == null )
			{
				throw new ArgumentNullException ( nameof ( area ) );
			}
			if ( money < 0 )
			{
				throw new ArgumentOutOfRangeException ( nameof ( money ) );
			}

			Money += money;
			Game . Current . GameEnviroment . PlayerSay ( this , Model . GetSayingWhenGained ( player?.Model ) );
			GetFromAreaEvent?.Invoke ( this , new PlayerGetFromAreaEventArgs ( area , player , money ) );
		}

		public event EventHandler<PlayerGetFromAreaEventArgs> GetFromAreaEvent;

		public override void EndToday ( )
		{
			if ( disposedValue )
			{
				throw new ObjectDisposedException ( nameof ( Player ) );
			}

			if ( State != PlayerState . Normal && StateStartDate != null && StateDuration != null )
			{
				if ( StateStartDate + StateDuration >= Game . Current . Calendar . Today . Date + 1 )
				{
					State = PlayerState . Normal;
				}
			}

			MoneyHistory . Add ( Money );

			//Todo:设立PlayerNextDay之类的时间然后加入MoneyHistory


		}

		public override void StartDay ( Calendars . GameDate nextDate )
		{
			if ( disposedValue )
			{
				throw new ObjectDisposedException ( nameof ( Player ) );
			}


			HaveMoveToday = false;


		}

		public void Bankruptcy ( )
		{
			foreach ( var item in Buffs )
			{
				item . Dispose ( );
			}
			Buffs . Clear ( );

			foreach ( var item in Areas )
			{
				AreaAuctionRequest request = new AreaAuctionRequest ( item , item . Price );
				Game . Current . GameEnviroment . PerformAuction ( request );
				//Todo:拍卖资产
			}
			foreach ( var item in Cards )
			{

			}

		}

		public Player ( PlayerModel model , long startMoney ) : base ( )
		{
			if ( model == null )
			{
				throw new ArgumentNullException ( nameof ( model ) );
			}
			if ( startMoney < 0 )
			{
				throw new ArgumentOutOfRangeException ( nameof ( startMoney ) );
			}
			Model = model;
			Money = startMoney;
		}

		protected override void Dispose ( bool disposing )
		{
			if ( !disposedValue )
			{
				if ( disposing )
				{

					Game . Current . GamePlayers . Remove ( this );

				}

				base . Dispose ( disposing );
			}
		}
	}

}